// (C) 2021 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.
	
module frame_loss_sm
#(
parameter           CONSECUTIVE_K28_1_Loss        =   3 ,              //Define how many K28.1 comma are consecutive detected loss
parameter           CONSECUTIVE_CRC_Loss          =   10               //Define how many CRC check are consecutive detected wrong             
)
(
input               clock,
input               reset_n,

input        [9:0]  lvds_rx_data,
input               frame_crc_err,
input               frame_loss_sm_en,
input               frame_comma_mismatch,
input        [3:0]  rx_frm_offset,

output  reg         frame_re_aligned,
output  reg         frame_lost

);

//////////////////////////////////////////////////////////////////////////////////
// States for FSM
//////////////////////////////////////////////////////////////////////////////////
localparam    ST_INIT                         = 4'd0;
localparam    ST_FRAME_LOSS_DETECTED          = 4'd1;
localparam    ST_FRAME_RE_ALIGNED             = 4'd2;
localparam    ST_FRAME_LOST                   = 4'd3;
	
	
//////////////////////////////////////////////////////////////////////////////////
// Parameters
//////////////////////////////////////////////////////////////////////////////////
localparam    LOW                             = 1'b0;
localparam    HIGH                            = 1'b1;

localparam    FRAME_COMMA_P                   = 10'b01_1000_0011;
localparam    FRAME_COMMA_N                   = 10'b10_0111_1100;
localparam    TOTAL_BITS_F                    = clog2(CONSECUTIVE_K28_1_Loss);
localparam    TOTAL_BITS_C                    = clog2(CONSECUTIVE_CRC_Loss);

//////////////////////////////////////////////////////////////////////////////////
// Internal Signals
//////////////////////////////////////////////////////////////////////////////////
reg	[3:0]	               rstate;	
 
reg   [(TOTAL_BITS_F-1):0] k28_1_loss_cnt;
reg   [(TOTAL_BITS_C-1):0] crc_loss_cnt;
reg   frame_comma_matched;

//////////////////////////////////////////////////////////////////////////////////
// Sequencial logic
//////////////////////////////////////////////////////////////////////////////////	

//frame_loss_sm
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
		frame_re_aligned                <=   LOW;
		frame_lost                      <=   LOW;
		
	   rstate                          <=   ST_INIT;		
	end
	
	else begin
	   case(rstate)
		   ST_INIT: begin
		      frame_re_aligned          <=   LOW;
				frame_lost                <=   LOW;
				
			   if(frame_loss_sm_en) begin
				   rstate                 <=   ST_FRAME_LOSS_DETECTED;
				end
			end //end case of ST_INIT
			
			ST_FRAME_LOSS_DETECTED: begin
			   frame_re_aligned          <=   LOW;
				frame_lost                <=   LOW;
				if(k28_1_loss_cnt == (CONSECUTIVE_K28_1_Loss-1) || crc_loss_cnt == (CONSECUTIVE_CRC_Loss-1)) begin
				   rstate                 <=   ST_FRAME_LOST;
				end
				else  if(rx_frm_offset == 4'd1 && frame_comma_matched && !frame_crc_err) begin
				   rstate                 <=   ST_FRAME_RE_ALIGNED;
				end
			
			end //end case of ST_FRAME_LOSS_DETECTED
			
			ST_FRAME_RE_ALIGNED: begin
            frame_re_aligned          <=   HIGH;
				frame_lost                <=   LOW;
			end //end case of ST_FRAME_RE_ALIGNED
			
			ST_FRAME_LOST: begin
            frame_re_aligned          <=   LOW;
				frame_lost                <=   HIGH;
				
				if(!frame_loss_sm_en) begin
				   rstate                 <=   ST_INIT;
				end
			end //end case of ST_FRAME_LOST
			
			default: begin
			   rstate                    <=   ST_INIT;
			end //end case of default
		endcase
	end
end


//CONSECUTIVE_K28_1_Loss times consecutive K28.1 loss counter
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   k28_1_loss_cnt                  <=   {TOTAL_BITS_F{1'b0}};
	end
	
	else begin
	   if(rstate == ST_FRAME_LOSS_DETECTED) begin
		   if(frame_comma_mismatch) begin
		      k28_1_loss_cnt            <=   k28_1_loss_cnt + 1'b1;
			end
		end
		else begin
		   k28_1_loss_cnt               <=   {TOTAL_BITS_F{1'b0}};
		end
	end
end
	
//CONSECUTIVE_CRC_Loss times consecutive CRC ERR counter
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   crc_loss_cnt                    <=   {TOTAL_BITS_C{1'b0}};
	end
	
	else begin
	   if(rstate == ST_FRAME_LOSS_DETECTED) begin
		   if(frame_crc_err) begin
		      crc_loss_cnt              <=   crc_loss_cnt + 1'b1;
			end
		end
		else begin
		   crc_loss_cnt                 <=   {TOTAL_BITS_C{1'b0}};
		end
	end
end

//Handle frame_comma_matched
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
       frame_comma_matched             <=   LOW;
    end
   
    else begin
      if(rx_frm_offset == 4'd0 && (lvds_rx_data == FRAME_COMMA_P || lvds_rx_data == FRAME_COMMA_N)) begin
           frame_comma_matched          <=   HIGH;
        end
        else if (rx_frm_offset == 4'd11) begin
           frame_comma_matched          <=   LOW;
        end
    end
end



//base 2 logarithm, run in precompile stage
function integer clog2;
input integer value;
begin
   value = (value > 1) ? value-1 : 1;            //this avoids clog returning 0, which would cause issues 
   for (clog2=0; value>0; clog2=clog2+1)
     value = value>>1;
end

endfunction

endmodule

